﻿/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using OptixDotNet;
using OptixDotNet.Math;
using OptixDotNet.Utils;

using OptixDotNetUtils;

using OptixTypes = OptixDotNet.Math;

namespace Sample
{
    public class Sample8 : OptixForm
    {        
        public Sample8()
        {
            //Ensure that we have a valid Nvidia GPU
            Utils.DetectGPUCompatibility();
        }

        protected override void  Initialize()
        {
            string selectorPath = Path.GetFullPath( "../../Assets/Cuda/selector_example.ptx" );
            string rayGenPath = Path.GetFullPath( "../../Assets/Cuda/pinhole_camera.ptx" );
            string shaderPath = Path.GetFullPath( "../../Assets/Cuda/normal_shader.ptx" );
            string cowModelPath = Path.GetFullPath( "../../Assets/Models/cow.obj" );
            string teapotModelPath = Path.GetFullPath( "../../Assets/Models/teapot.obj" );

            /*-----------------------------------------------
             * Create the Optix context
             *-----------------------------------------------*/
            Context = new Context();
            Context.RayTypeCount = 1;
            Context.EntryPointCount = 1;

            /*-----------------------------------------------
             * Create the material that will be executed when there is an intersection
             *-----------------------------------------------*/
            Material material = new Material( Context );
            material.Programs[ 0 ] = new SurfaceProgram( Context, RayHitType.Closest, shaderPath, "closest_hit_radiance" ); 

            /*-----------------------------------------------
             * Create the selector node
             *-----------------------------------------------*/
            Selector selector = new Selector( Context );
            selector.VisitProgram = new Program( Context, selectorPath, "visit" );

            /*-----------------------------------------------
             * Load the geometry
             *-----------------------------------------------*/
            OptixOBJLoader model = new OptixOBJLoader( cowModelPath, Context, null, material );
            model.GeoGroup = new GeometryGroup( Context );
            model.Material = material;
            model.Scale = 100.0f;

            string intersectPath = Path.GetFullPath( "../../Assets/Cuda/triangle_mesh.ptx" );
            model.IntersecitonProgPath = intersectPath;
            model.BoundingBoxProgPath = intersectPath;
            model.IntersecitonProgName = "mesh_intersect";
            model.BoundingBoxProgName = "mesh_bounds";
            model.LoadContent();

            selector.AddChild( model.GeoGroup );

            model.FileName = teapotModelPath;
            model.GeoGroup = new GeometryGroup( Context );
            model.Scale = .075f;
            model.LoadContent();

            selector.AddChild( model.GeoGroup );

            /*-----------------------------------------------
             * Create the output buffer
             *-----------------------------------------------*/
            CreateOutputBuffer( Format.UByte4 );

            /*-----------------------------------------------
             * Create the ray-generation and exception programs
             *-----------------------------------------------*/
            Program rayGen = new Program( Context, rayGenPath, "pinhole_camera" );
            Program exception = new Program( Context, rayGenPath, "exception" );
            Program miss = new Program( Context, shaderPath, "miss" );
            miss[ "bg_color" ].Set( 100 / 255.0f, 149 / 255.0f, 237 / 255.0f );

            Context.SetRayGenerationProgram( 0, rayGen );
            Context.SetExceptionProgram( 0, exception );
            Context.SetRayMissProgram( 0, miss );

            /*-----------------------------------------------
             * Finally compile the optix context, and build the accel tree
             *-----------------------------------------------*/
            SetCamera( model.BBox );

            Context[ "top_object" ].Set( selector );
            Context[ "output_buffer" ].Set( OutputBuffer );
            Context[ "scene_epsilon" ].Set( 0.001f );
            Context[ "radiance_ray_type" ].Set( 0u );
            Context[ "bad_color" ].SetFloat3( new Vector3( 1.0f, 0.0f, 0.0f ) );

            Context.Compile();
            Context.BuildAccelTree();

            RaysTracedPerFrame = Width * Height;
        }

        private void SetCamera( BoundingBox box )
        {
            Camera = new Camera();
            Camera.Aspect = (float)Width / (float)Height;
            Camera.Fov = 30.0f;
            Camera.RotationVel = 100.0f;
            Camera.TranslationVel = 400.0f;

            Camera.CenterOnBoundingBox( box );

            CameraUpdate();
        }

        protected override void RayTrace()
        {
            Context.Launch( 0, (uint)Width, (uint)Height );
        }

        protected override void Display()
        {
            base.Display();
        }

        protected override void CameraUpdate()
        {
            Vector3 eye = Camera.Position;
            Vector3 right = Camera.Right;
            Vector3 up = Camera.Up;
            Vector3 look = Camera.Look;

            Context[ "eye" ].Set( ref eye );
            Context[ "U" ].Set( ref right );
            Context[ "V" ].Set( ref up );
            Context[ "W" ].Set( ref look );
        }
    }
}
